﻿using System.Reflection;

namespace Admin.NET.Application.Service;

/// <summary>
/// 前端dto代码生成
/// </summary>
[ApiDescriptionSettings(KeepName = true)]
public class DtoTsGenService : IDynamicApiController
{
    private readonly SqlSugarRepository<BasEnum> _rep;

    public DtoTsGenService(SqlSugarRepository<BasEnum> repository)
    {
        _rep = repository;
    }

    /// <summary>
    /// 根据枚举表生成前端的字符串常量代码
    /// </summary>
    public void CreateConstFromEnum()
    {
        CodeGen2ConstTs.Create(_rep);
    }


    /// <summary>
    /// 常规开发过程中不需要此功能,使用CreateByEntity
    /// 遍历命名空间内全部的DTO(Input或者Output结尾的类)生成ts的dto, 
    /// 生成的TS文件在 TestProject1\CodeGen 目录下面
    /// </summary>
    /// <param name="namespaceName">Admin.NET.Application.Service</param>
    public void CreateAll(string namespaceName = "Admin.NET.Application.Service")
    {
        Assembly assembly = Assembly.GetExecutingAssembly();

        // 获取指定命名空间下的所有类型
        var types = assembly.GetTypes().Where(t => String.Equals(t.Namespace, namespaceName, StringComparison.Ordinal));

        var indexCode = "";
        foreach (var type in types)
        {
            if (type.Name.EndsWith("Input") || type.Name.EndsWith("Output"))
            {
                //创建dto文件
                var code = CreateDto(type);
                var filePath = Path.Combine(GetTargetPath(), type.Name + ".ts");
                File.WriteAllText(filePath, code);

                //插入index.ts
                indexCode += $"export * from './{type.Name}';\r\n";
            }
        }

        //插入index.ts
        var indexPath = Path.Combine(GetTargetPath(), "index.ts");
        File.WriteAllText(indexPath, indexCode);
    }


    /// <summary>
    /// 根据实体名生成dto
    /// 生成的TS文件在 TestProject1\CodeGen 目录下面
    /// </summary>
    /// <param name="entityName"></param>
    /// <param name="namespaceName"></param>
    public void CreateByEntity(string entityName, string namespaceName = "Admin.NET.Application.Service")
    {
        Assembly assembly = Assembly.GetExecutingAssembly();

        // 获取指定命名空间下的所有类型
        var types = assembly.GetTypes().Where(t => String.Equals(t.Namespace, namespaceName, StringComparison.Ordinal));
        // 找到当前entity的DTO
        // 创建dto.ts
        var dtoPath = GetDtoPath(entityName);
        var importType = "";
        var exportType = "";
        foreach (var type in types)
        {
            if (type.Name.EndsWith("Input") || type.Name.EndsWith("Output"))
            {
                if (type.Name.StartsWith(entityName))
                {
                    //创建dto文件
                    var code = CreateDto(type);
                    var filePath = Path.Combine(dtoPath, type.Name + ".ts");
                    File.WriteAllText(filePath, code);

                    importType += $"import {{ {type.Name} }} from './dto/{type.Name}';\r\n";
                    exportType += $" {type.Name},";
                }
            }
        }

        // 创建api.ts
        // TODO 根据Service生成前端api文件
        var apiPath = GetApiIndexPath(entityName);
        var apiCode = apiIndexTemplate.Replace("{0}", entityName);
        apiCode = apiCode.Replace("{1}", importType);
        apiCode = apiCode.Replace("{2}", exportType);
        File.WriteAllText(apiPath, apiCode);
    }

    // 创建dto.ts的内容
    private string CreateDto(Type type)
    {
        PropertyInfo[] properties = type.GetProperties();

        var code = "export interface " + type.Name + " {";
        code += "\r\n";
        foreach (PropertyInfo property in properties)
        {
            // 检查属性是否有公共的get和set访问器
            bool hasPublicGet = property.GetMethod != null && property.GetMethod.IsPublic;
            bool hasPublicSet = property.SetMethod != null && property.SetMethod.IsPublic;

            if (hasPublicGet && hasPublicSet)
            {
                Type propertyType = property.PropertyType;
                string propertyTypeName = propertyType.Name;
                // 检查是否为可空类型
                if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                {
                    Type underlyingType = Nullable.GetUnderlyingType(propertyType);
                    propertyTypeName = underlyingType.Name;

                    code += $"  {LowercaseFirstLetter(property.Name)}?:";
                }
                else
                {
                    code += $"  {LowercaseFirstLetter(property.Name)}:";
                }
                string tsType = "string";
                switch (propertyTypeName)
                {
                    case "Int64":
                    case "Int32":
                    case "Double":
                    case "Decimal":
                    case "Float":
                        tsType = "number";
                        break;
                    case "Boolean":
                        tsType = "boolean";
                        break;
                    case "DateTime":
                        tsType = "Date";
                        break;
                    case "String":
                        tsType = "string";
                        break; 
                    case "List`1":
                        tsType = "[]";
                        break;
                    default:
                        tsType = propertyType.Name;
                        break;
                };
                code += $" {tsType};";
                code += "\r\n";
            }
        }
        code += "}";

        return code;
    }

    // 获取生成的文件的存放路径
    private static string GetTargetPath()
    {
        var backendPath = Path.Combine(new DirectoryInfo(App.WebHostEnvironment.ContentRootPath).Parent.FullName, "CodeGen");
        if (!Directory.Exists(backendPath))
            Directory.CreateDirectory(backendPath);
        return backendPath;
    }

    // dto路径: api/entityName/dto
    private string GetDtoPath(string entityName)
    {
        string path = GetTargetPath();
        if (!string.IsNullOrEmpty(entityName))
        {
            path = Path.Combine(path, $"{entityName}");
            path = Path.Combine(path, "dto");
        }
        if (!Directory.Exists(path))
            Directory.CreateDirectory(path);

        return path;
    }

    // dto的index路径: api/entityName
    private string GetApiIndexPath(string entityName)
    {
        string path = GetTargetPath();
        if (!string.IsNullOrEmpty(entityName))
        {
            path = Path.Combine(path, $"{entityName}");
        }
        if (!Directory.Exists(path))
            Directory.CreateDirectory(path);

        return Path.Combine(path, "index.ts");
    }

    // 首字母转小写
    private static string LowercaseFirstLetter(string str)
    {
        if (string.IsNullOrEmpty(str))
            return str;

        return char.ToLower(str[0]) + str.Substring(1);
    }

    // 前端api的代码模板
    private static string apiIndexTemplate = @"
import { axiosGet, axiosPost, axiosPut } from '/@/api/RequestBase';
import { SqlSugarPagedList, BaseKeywordPageInput } from '/@/api/BaseDto';

{1}
export type {{2} };

const service = '{0}/'; // 要写后面的斜杠, 不要写前面的斜杠

export const {0}Api = {
	page: (data: BaseKeywordPageInput) => {
		return axiosPost<SqlSugarPagedList<{0}Output>>(service + 'page', data);
	},
	get: (id: number) => {
		return axiosGet<{0}Output>(service + `get/${id}`);
	},
	add: (data: {0}Input) => {
		return axiosPost<number>(service + 'add', data);
	},
	update: (data: {0}Input) => {
		return axiosPut<boolean>(service + 'update', data);
	},
}
";

}